package com.smoed.common.utilities.unitconversions;

public enum ByteUnitConversions {
    BYTE(1, "bytes"),
    KILOBYTE(1024, "KB"),                   // 2^10 bytes = 1,024 bytes
    MEGABYTE(1048576, "MB"),                // 2^20 bytes = 1,048,576 bytes
    GIGABYTE(1073741824, "GB"),             // 2^30 bytes = 1,073,741,824 bytes
    TERABYTE(1099511627776L, "TB"),         // 2^40 bytes = 1,099,511,627,776 bytes
    PETABYTE(1125899906842624L, "PB"),      // 2^50 bytes = 1,125,899,906,842,624 bytes
    EXABYTE(1152921504606846976L, "EB");    // 2^60 bytes = 1,152,921,504,606,846,976 bytes

    private final long numBytes;
    private final String toString;

    private ByteUnitConversions(long numBytes, String toString) {
        this.numBytes = numBytes;
        this.toString = toString;
    }

    public long getNumBytes() {
        return numBytes;
    }

    public long getNumBits() {
        return numBytes / 8;
    }

    public double convertTo(ByteUnitConversions unit, long value) {
        final long numBytes = getNumBytes() * value;
        switch(unit) {
        case KILOBYTE:
            return numBytes / (double) KILOBYTE.getNumBytes();
        case MEGABYTE:
            return numBytes / (double) MEGABYTE.getNumBytes();
        case GIGABYTE:
            return numBytes / (double) GIGABYTE.getNumBytes();
        case TERABYTE:
            return numBytes / (double) TERABYTE.getNumBytes();
        case PETABYTE:
            return numBytes / (double) PETABYTE.getNumBytes();
        case EXABYTE:
            return numBytes / (double) EXABYTE.getNumBytes();
        }

        return numBytes;
    }

    public ByteUnitConversions getOptimalUnit(long value) {
		final long numBytes = getNumBytes() * value;

		final ByteUnitConversions[] values = ByteUnitConversions.values();
		for (int i = 1; i < values.length; i++) {
			if (values[i].numBytes > numBytes)
				return values[i-1];
		}

		return null;
	}

    public String getConvenientRepresentation(long value) {
		final ByteUnitConversions optimalUnit = this.getOptimalUnit(value);
		final double optimalValRep = this.convertTo(optimalUnit, value);

		return String.format("%,.2f " + optimalUnit.toString, optimalValRep);
	}

    private static final double BITS_TO_BYTES_CONVERSION = 8.0;
    public double fromBits(long numBits) {
        final long numBytes = (long) Math.ceil(numBits / BITS_TO_BYTES_CONVERSION);
        return ByteUnitConversions.BYTE.convertTo(this, numBytes);
    }
}
